package com.analyst.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.*;



@Data
@AllArgsConstructor
public class Job implements Runnable, Comparable {
    @TableId(type = IdType.AUTO)
    private String id;

    private String sqlString;
    private Date beginTime;
    private Date endTime;
    private long pendingTime;
    private int level;
    private boolean highLight;
    private boolean update;
    @TableField(exist = false)
    private PriorityQueue<Job> dependencies=new PriorityQueue<>(
            (a,b)->
                    Math.toIntExact(b.endTime.getTime() - a.endTime.getTime())
    );
    @TableField(exist = false)
    private List<Job> nextList=new ArrayList<>();




    private CountDownLatch latch;
    public Job(String id, String sqlString, Date beginTime, Date endTime) {
        this.id = id;
        this.sqlString = sqlString;
        this.beginTime = beginTime;
        this.endTime = endTime;
    }

    private long improveTime;
    JobStatus status=JobStatus.NEW ;





    public Duration getDuration(){
        LocalDateTime b =beginTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime e =endTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        return Duration.between(b,e);
    }



    public Job(String jobString, Date beginTime, Date endTime) {
        this.sqlString = jobString;
        this.beginTime = beginTime;
        this.endTime = endTime;
    }

    @Override
    public String toString() {
        return "Job{" +
                "index=" + id +
                ", job=" + sqlString +
                ", beginTime=" + beginTime +
                ", endTime=" + endTime +
                '}';
    }

    public Job() {
    }

    private static final ExecutorService executorService =   new ThreadPoolExecutor
            (4,8, 0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(100));

    public void start(){

        executorService.submit(this);
    }

    @Override
    public void run() {
        //判断是否job状态更新
        if (this.status==JobStatus.DONE){
            return;
        }
        synchronized (this){
            //获取到锁 其他线程有无更新该job
            if (this.status==JobStatus.DONE)
                return;
            //等待其他前继节点线程都更新完成
            try {
                waitUntilDone();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (TimeoutException e) {
                System.out.println("Job " + getSqlString() + " waiting for predecessor timeout.");
            }
            //开始更新
            if (!Thread.currentThread().isInterrupted()) {
                update();
                setStatus(JobStatus.DONE);
                if (getNextList().isEmpty()) {

                    latch.countDown();
                    executorService.shutdown();
                    return;
                }
                // 使用的线程池执行更新后继节点
                for (Job successor : getNextList()) {
                    successor.setLatch(latch);
                    executorService.submit(successor);
                }
            }

        }



    }
//     latch.countDown();
//        System.out.println("latch +"+  "job "+this.getSqlString()+"  "+  latch.getCount());
//        System.out.println("该线程更新完毕：："+  "job "+this.getSqlString()+"  " +this.getBeginTime()+this.getEndTime());


    public void update() {

        // 实现在兄弟集合中的更新排序
        setEndTime(new Date( endTime.getTime()-improveTime));
        setBeginTime(new Date( beginTime.getTime()-improveTime));
        //排序
        for (Job next : getNextList()) {

            next.getDependencies().remove(this);
            next.getDependencies().offer(this);
        }

    }

    private void waitUntilDone() throws InterruptedException ,TimeoutException {
            long time=0;
            while (true) {
                boolean done = true;
                for (Job predecessor : getDependencies()) {
                    if (predecessor.getStatus() == JobStatus.ON_UPDATE) {
                        done = false;
                        break;
                    }
                }
                if (done) {
                    if (getDependencies().size()==0){
                        return;
                    }
                    long newBeginTime = getDependencies().peek().getEndTime().getTime() + this.getPendingTime();
                    improveTime = this.getBeginTime().getTime()-newBeginTime;
                    return;
                }
                Thread.sleep(500); // 等待1秒钟
                if (++time>=10){
                    throw new TimeoutException();
                }

            }
        }


    @Override
    public int compareTo(Object o) {
        Job o1 = (Job) o;
        return (int) (o1.getEndTime().getTime() - o1.getBeginTime().getTime() - (getEndTime().getTime() - getBeginTime().getTime()));
    }
}
